//
// Created by daybeha on 2022/7/8.
//

#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <iostream>
#include <opencv2/opencv.hpp>
#include "numpy/arrayobject.h"

using namespace std;


const char * python_modle= "sg_match";
const char * python_func =  "compute_score";
//const char * python_func =  "con_show";
//const char * python_func =  "show_img";


PyObject* Mat2Numpy(cv::Mat img){
    int r = img.rows;
    int c = img.cols;
    int ch = img.channels();
    // total number of elements (here it's an RGB image of size 640x480)
    int nElem = r * c * ch;

    // 创建数组类型，保存上面读到的图片
    // create an array of apropriate datatype
    uchar* m = new uchar[nElem];
    // copy the data from the cv::Mat object into the array
    std::memcpy(m, img.data, nElem * sizeof(uchar));

//    // the dimensions of the matrix
//    npy_intp mdim[] = { r, c, ch};
//    // 将cv::Mat 转为 numpy.array 类型 convert the cv::Mat to numpy.array
//    PyObject* mat = PyArray_SimpleNewFromData(ch, mdim, NPY_UINT8, (void*) m);

    // the dimensions of the matrix
    PyObject* mat;
    cout << "mdim : " << endl;
    if(ch == 1){
        npy_intp mdim[2] = {r, c};
        cout << mdim[0] << ", " << mdim[1] << ", "<< mdim[2] <<endl;
        // 将cv::Mat 转为 numpy.array 类型 convert the cv::Mat to numpy.array
        mat = PyArray_SimpleNewFromData(2, mdim, NPY_UINT8, (void*) m);
    }else{
        npy_intp mdim[3] = {r, c, ch};
        cout << mdim[0] << ", " << mdim[1] << ", "<< mdim[2] <<endl;
        mat = PyArray_SimpleNewFromData(3, mdim, NPY_UINT8, (void*) m);
    }

    delete[] m;
    return mat;
}


float compute_match_score(cv::Mat& img0, cv::Mat& img1, PyObject* pFunc){
//        vector<cv::Mat> imgs;
//        imgs.emplace_back(img0);
//        imgs.emplace_back(img1);
//        cv::Mat con_img = ImageSplicing(imgs,1);
//        cv::imshow("con_img", con_img);
//        cv::waitKey();

    float score=-1;
    PyObject *mat0 = Mat2Numpy(img0);
    PyObject *mat1 = Mat2Numpy(img1);

    PyObject *pArgs = PyTuple_New(2);     // 必须先创建Tuple
    PyTuple_SetItem(pArgs, 0, mat0);
    PyTuple_SetItem(pArgs, 1, mat1);

//        PyObject *pArgs = PyTuple_New(1);     // 必须先创建Tuple
//        PyTuple_SetItem(pArgs, 0, PyLong_FromLong(5));

    PyObject *result = PyEval_CallObject(pFunc, pArgs);

    if (result != NULL) {
        score = PyFloat_AsDouble(result);
        printf("Result of call: %f\n",score);
        Py_DECREF(result);
    } else {
        cerr << "result Null" << endl;
    }

    // 释放引用 decrement the object references
    Py_XDECREF(mat0);
    Py_XDECREF(mat1);
    Py_XDECREF(result);
    Py_XDECREF(pArgs);

    return score;
}

void loadPython(){
    PyObject *pModule, *pFunc;
    // 初始化
    Py_Initialize();

    PyRun_SimpleString("import sys\n");
//    PyObject *sys_path = PySys_GetObject("path");
//    PyList_Append(sys_path, PyUnicode_FromString("/home/daybeha/Documents/github/DeepLabV3_ws/src/superglue/script"));
    PyRun_SimpleString("sys.path.append('/home/daybeha/Documents/github/DeepLabV3_ws/src/superglue/script')");
    PyRun_SimpleString("print(sys.path)");

    // 检查当前Python版本
    PyRun_SimpleString("import platform\n"
                       "print(f\"\\033[92m Python Version: {platform.python_version()}\\033[0m\")\n");

    // 在C中使用numpy之前一定要先 import_array()
    // https://numpy.org/doc/1.13/reference/c-api.array.html#c.import_array
    // this macro is defined be NumPy and must be included
//    import_array1(-1);
    _import_array();

    // 导入 python 模块
    PyObject *pName = PyUnicode_DecodeFSDefault(python_modle);
    /* Error checking of pName left out */
    pModule = PyImport_Import(pName);
//    pModule = PyImport_ImportModule(python_modle);


//    PyRun_SimpleString("import os\n"
//                       "print(f'current path:{os.getcwd()}')\n");

    if (pModule != NULL) {
        cout << "loaded " << python_modle << endl;

        // 获取目标python函数
//        // get dictionary of all available entities in the module
//        pDict = PyModule_GetDict(pModule);
//        // grab the functions we are interested in (using its name)
//        pFunc = PyDict_GetItemString(pDict, "foo_bar");
        pFunc = PyObject_GetAttrString(pModule, python_func);

        /* pFunc is a new reference */
        if (pFunc && PyCallable_Check(pFunc)) {
            // 读取图片
            cv::Mat img0 = cv::imread("/home/daybeha/Documents/Dataset/Kitti/sequences/00/image_0/000000.png",cv::IMREAD_GRAYSCALE);
            cv::Mat img1 = cv::imread("/home/daybeha/Documents/Dataset/Kitti/sequences/00/image_0/000020.png",cv::IMREAD_GRAYSCALE);

            float score = compute_match_score(img0, img1, pFunc);
            cout << "score: " << score << endl;
        }
        else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", python_func);
        }
        Py_XDECREF(pFunc);
        Py_DECREF(pModule);
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", python_modle);

        return;
    }

    Py_Finalize();
}

int main(int argc, char *argv[])
{
    loadPython();
    return 0;

}